﻿using System;
using System.IO;
using System.Runtime.InteropServices;

namespace maranov.SymlinkManager.FilesystemLinks
{
    public partial class HardLink
    {
        /// <summary>
        /// Establishes a hard link between an existing file and a new file.
        /// This function is only supported on the NTFS file system, and only for files, not directories.
        /// </summary>
        /// <param name="lpFileName">
        /// The name of the new file.
        /// This parameter cannot specify the name of a directory.
        /// </param>
        /// <param name="lpExistingFileName">
        /// The name of the existing file.
        /// This parameter cannot specify the name of a directory.
        /// </param>
        /// <param name="lpSecurityAttributes">
        /// Reserved; must be NULL.
        /// Use CreateHardlinkSecurityAttributes.
        /// </param>
        /// <returns>
        /// If the function succeeds, the return value is nonzero.
        /// If the function fails, the return value is zero (0).
        /// To get extended error information, call GetLastError.
        /// </returns>
        [DllImport("Kernel32", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern bool CreateHardLink(
            [In] string lpFileName,
            [In] string lpExistingFileName,
            IntPtr lpSecurityAttributes
            );

        /// <summary>
        /// CreateHardLink default lpSecurityAttributes value.
        /// </summary>
        public readonly static IntPtr CreateHardlinkSecurityAttributes = IntPtr.Zero;

        /// <summary>
        /// Removes existing hard link.
        /// </summary>
        /// <param name="path">Path to the link.</param>
        public static void RemoveHardlink(string path)
        {
            try
            {
                if (Directory.Exists(path))
                    throw new Exception();
                else
                    File.Delete(path);
            }
            catch (IOException e)
            {
                throw new LinkRemovalException("Hard link removal failed.", e);
            }
        }

        /// <summary>
        /// Retrieves file information for the specified file.
        /// </summary>
        /// <param name="hFile">
        /// A handle to the file that contains the information to be retrieved.
        /// This handle should not be a pipe handle.
        /// </param>
        /// <param name="lpFileInformation">
        /// A pointer to a BY_HANDLE_FILE_INFORMATION structure that receives the file information.
        /// </param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetFileInformationByHandle(
            IntPtr hFile,
            out BY_HANDLE_FILE_INFORMATION lpFileInformation);

        /// <summary>
        /// Contains information that the GetFileInformationByHandle function retrieves.
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        struct BY_HANDLE_FILE_INFORMATION
        {
            /// <summary>
            /// The file attributes. For possible values and their descriptions, see File Attribute Constants.
            /// </summary>
            public uint FileAttributes;

            /// <summary>
            /// A FILETIME structure that specifies when a file or directory is created. If the underlying file system does not support creation time, this member is zero (0).
            /// </summary>
            public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;

            /// <summary>
            /// A FILETIME structure. For a file, the structure specifies the last time that a file is read from or written to. For a directory, the structure specifies when the directory is created. For both files and directories, the specified date is correct, but the time of day is always set to midnight. If the underlying file system does not support the last access time, this member is zero (0).
            /// </summary>
            public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;

            /// <summary>
            /// A FILETIME structure. For a file, the structure specifies the last time that a file is written to. For a directory, the structure specifies when the directory is created. If the underlying file system does not support the last write time, this member is zero (0).
            /// </summary>
            public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;

            /// <summary>
            /// The serial number of the volume that contains a file.
            /// </summary>
            public uint VolumeSerialNumber;

            /// <summary>
            /// The high-order part of the file size.
            /// </summary>
            public uint FileSizeHigh;

            /// <summary>
            /// The low-order part of the file size.
            /// </summary>
            public uint FileSizeLow;

            /// <summary>
            /// The number of links to this file. For the FAT file system this member is always 1. For the NTFS file system, it can be more than 1.
            /// </summary>
            public uint NumberOfLinks;

            /// <summary>
            /// The high-order part of a unique identifier that is associated with a file. For more information, see nFileIndexLow.
            /// </summary>
            public uint FileIndexHigh;

            /// <summary>
            /// The low-order part of a unique identifier that is associated with a file.
            /// The identifier (low and high parts) and the volume serial number uniquely identify a file on a single computer. To determine whether two open handles represent the same file, combine the identifier and the volume serial number for each file and compare them.
            /// The ReFS file system, introduced with Windows Server 2012, includes 128-bit file identifiers. To retrieve the 128-bit file identifier use the GetFileInformationByHandleEx function with FileIdInfo to retrieve the FILE_ID_INFO structure. The 64-bit identifier in this structure is not guaranteed to be unique on ReFS.
            /// </summary>
            public uint FileIndexLow;
        }
    }
}
